package service

import (
	"boy-go/modules/system/model"
	"boy-go/modules/system/repository"
	"boy-go/modules/system/vo"
	"boy-go/pkg/auth"
	"boy-go/pkg/config"
	"boy-go/pkg/constants"
	"boy-go/pkg/iputils"
	"boy-go/pkg/redis"
	"boy-go/pkg/security"
	"boy-go/pkg/xerror"
	"boy-go/pkg/xstr"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/copier"
	"github.com/ua-parser/uap-go/uaparser"
	"time"
)

func NewLoginService() LoginService {
	return &loginService{
		SysUserRepo:       repository.NewSysUserRepository(),
		SysDeptRepo:       repository.NewSysDeptRepository(),
		SysRoleRepo:       repository.NewSysRoleRepository(),
		SysPostRepo:       repository.NewSysPostRepository(),
		SysConfigRepo:     repository.NewSysConfigRepository(),
		SysLogininforRepo: repository.NewSysLogininforRepository(),
	}
}

type loginService struct {
	SysUserRepo       *repository.SysUserRepository
	SysDeptRepo       *repository.SysDeptRepository
	SysRoleRepo       *repository.SysRoleRepository
	SysPostRepo       *repository.SysPostRepository
	SysConfigRepo     *repository.SysConfigRepository
	SysLogininforRepo *repository.SysLogininforRepository
}

// 登录
func (s *loginService) Login(ctx *gin.Context, loginBody *vo.LoginBody) (*string, error) {
	//TODO
	err := s.validateCaptcha(ctx, loginBody.Uuid, loginBody.Code)
	if err != nil {
		return nil, err
	}
	err = s.loginPreCheck(loginBody.Username, loginBody.Password)
	if err != nil {
		return nil, err
	}
	sysUser, err := s.SysUserRepo.GetByName(loginBody.Username)
	if err != nil {
		return nil, err
	}
	if sysUser == nil {
		return nil, xerror.ErrNilAccount
	}
	//密码校验
	err = security.MatchesPassword(loginBody.Password, sysUser.Password)
	if err != nil {
		s.AddLogininfor(ctx, loginBody.Username, "1", "用户名或密码错误")
		return nil, err
	}
	if err != nil {
		return nil, fmt.Errorf("token生成失败")
	}
	loginUser := auth.LoginUser{
		UserId:   sysUser.UserId,
		DeptId:   *sysUser.DeptId,
		UserName: *sysUser.UserName,
	}
	user := new(vo.SysUserModel)
	err = copier.Copy(user, sysUser)
	if err == nil {
		dept, err := s.SysDeptRepo.GetById(*sysUser.DeptId)
		if err == nil {
			deptModel := new(vo.SysDeptModel)
			err := copier.Copy(deptModel, dept)
			if err == nil {
				user.Dept = deptModel
			}
		}
		roles, err := s.SysRoleRepo.SelectRoleByUserId(sysUser.UserId)
		if err == nil {
			roleModels := make([]*vo.SysRoleModel, 0)
			roleIds := make([]*int64, 0)
			for _, role := range roles {
				roleModel := new(vo.SysRoleModel)
				roleIds = append(roleIds, &role.RoleId)
				if err := copier.Copy(roleModel, role); err != nil {
					continue
				}
				roleModels = append(roleModels, roleModel)
			}
			user.RoleIds = roleIds
			user.Roles = roleModels
		}
		postIds, err := s.SysPostRepo.SelectPostListByUserId(sysUser.UserId)
		if err == nil {
			user.PostIds = postIds
		}
		loginUser.User = user
	}
	permissions := NewSysPermissionService().GetMenuPermissionMap(loginUser.User)
	loginUser.Permissions = permissions
	s.recordLoginInfo(ctx, sysUser)
	token := NewTokenService().GenerateToken(ctx, loginUser)
	s.AddLogininfor(ctx, loginBody.Username, "0", "登陆成功")
	return &token, nil
}
func (s *loginService) validateCaptcha(ctx *gin.Context, uuid *string, code *string) error {
	captchaEnabled := NewSysConfigService().SelectCaptchaEnabled()
	if captchaEnabled {
		if xstr.IsEmpty(uuid) || xstr.IsEmpty(code) {
			return xerror.ErrParameterError
		}
		verifyKey := constants.CAPTCHA_CODE_KEY + *uuid
		captcha := redis.GetValue(ctx, verifyKey)
		if xstr.IsEmpty(captcha) {
			return xerror.NewArgument("验证码无效")
		}
		if *captcha != *code {
			return xerror.NewArgument("验证码错误")
		}
		redis.Delete(ctx, verifyKey)
	}
	return nil
}

// 登录预检查
func (s *loginService) loginPreCheck(username string, password string) error {
	if xstr.IsEmpty(&username) || xstr.IsEmpty(&password) {
		return xerror.ErrParameterError
	}
	return nil
}

func (s *loginService) recordLoginInfo(ctx *gin.Context, user *model.SysUser) {
	loginTime := time.Now()
	ip := iputils.GetIp(ctx)
	sysUser := &model.SysUser{
		UserId:    user.UserId,
		UpdateAt:  &loginTime,
		LoginDate: &loginTime,
		LoginIp:   &ip,
	}
	err := s.SysUserRepo.Update(sysUser)
	if err != nil {
		fmt.Printf("更新登陆信息失败：%s", err)
	}
}

func (s *loginService) AddLogininfor(ctx *gin.Context, username string, status string, msg string) {
	ip := iputils.GetIp(ctx)
	location := ""
	addressEnabled := config.GetBoyGoConfig().AddressEnabled
	if addressEnabled {
		location = iputils.GetRealAddressByIP(ip)
	}
	browser := ""
	os := ""
	time := time.Now()
	// 解析 User-Agent
	uaString := ctx.GetHeader("User-Agent")

	logininfor := model.SysLogininfor{
		UserName:      &username, // 用户账号
		Ipaddr:        &ip,       // 登录IP地址
		LoginLocation: &location, // 登录地点
		Browser:       &browser,  // 浏览器类型
		Os:            &os,       // 操作系统
		Status:        &status,   // 登录状态（0成功 1失败）
		Msg:           &msg,      // 提示消息
		LoginAt:       &time,     // 访问时间
	}
	if uaString != "" {
		// 使用 uap-go 解析 User-Agent 字符串
		parser := uaparser.NewFromSaved()
		ua := parser.Parse(uaString)
		logininfor.Browser = &ua.UserAgent.Family
		logininfor.Os = &ua.Os.Family
	}
	s.SysLogininforRepo.Save(&logininfor)
}
